// Copyright (c) 2009 Mike Haight, Andy Kipp
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#include "stdafx.h"

LRESULT CALLBACK WindowNativeCallbackForwarder(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return (LRESULT)Window::RaiseWindowMessage(hWnd, uMsg, wParam, lParam).ToPointer();
}


Window::Window(String^ argName, Coordinates::Rectangle argWindowRect)
{
    // Register a window class
    IntPtr nativeClassName = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(String::Format("{0}<{1}>", GetType()->Name, GetHashCode()));
    try
    {
        WNDCLASSEXW windowClass;

        windowClass.cbSize = sizeof(windowClass);
        windowClass.cbClsExtra = 0;
        windowClass.cbWndExtra = 0;
        windowClass.hbrBackground = NULL;
        windowClass.hCursor = NULL;
        windowClass.hIcon = NULL;
        windowClass.hIconSm = NULL;
        windowClass.hInstance = GetModuleHandleW(NULL);
        windowClass.lpfnWndProc = WindowNativeCallbackForwarder;
        windowClass.lpszClassName = (LPCWSTR)nativeClassName.ToPointer();
        windowClass.lpszMenuName = NULL;
        windowClass.style = 0;

        if (NULL == (mClassAtom = ::RegisterClassExW(&windowClass)))
            throw gcnew Exception("RegisterClassExW");
    }
    finally
    {
        System::Runtime::InteropServices::Marshal::FreeHGlobal(nativeClassName);
    }

    // Create a window
    IntPtr nativeName = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(argName);
    try
    {
        if (NULL == (mNativeHandle = ::CreateWindowExW(
            (NULL),
            (LPCWSTR)MAKELONG(mClassAtom, 0),
            (LPCWSTR)nativeName.ToPointer(),
            (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU),
            (int)(argWindowRect.X),
            (int)(argWindowRect.Y),
            (int)(argWindowRect.Width),
            (int)(argWindowRect.Height),
            (NULL),
            (NULL),
            (GetModuleHandle(NULL)),
            (NULL))))
            throw gcnew Exception("CreateWindowExW");
    }
    finally
    {
        System::Runtime::InteropServices::Marshal::FreeHGlobal(nativeName);
    }

    RECT clientRect = {0};
    ::GetClientRect(mNativeHandle, &clientRect);

    ::SetWindowPos(
        mNativeHandle,
        NULL,
        0,
        0,
        2 * (int)argWindowRect.Width - (clientRect.right - clientRect.left),
        2 * (int)argWindowRect.Height - (clientRect.bottom - clientRect.top),
        SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW);

    ::ShowWindow(mNativeHandle, SW_SHOW);

    // Save the window to the dictionary
    smWindows.Add(IntPtr(mNativeHandle), this);
}

Window::~Window(void)
{
    ::DestroyWindow(mNativeHandle);
    ::UnregisterClassW((LPCWSTR)MAKELONG(mClassAtom, 0), GetModuleHandle(NULL));
}

IntPtr Window::Handle::get(void)
{
    return IntPtr(mNativeHandle);
}

int Window::ViewWidth::get(void)
{
    RECT rect;
    ::GetClientRect(mNativeHandle, &rect);
    return rect.right - rect.left;
}

int Window::ViewHeight::get(void)
{
    RECT rect;
    ::GetClientRect(mNativeHandle, &rect);
    return rect.bottom - rect.top;
}

IntPtr Window::WindowProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CLOSE:
        {
            // Temporary:
            ::PostQuitMessage(0);
            break;
        }

        case WM_KILLFOCUS:
        {
            this->KillFocus(this, EventArgs::Empty);
            break;
        }

        case WM_SETFOCUS:
        {
            this->SetFocus(this, EventArgs::Empty);
            break;
        }

        case WM_SIZE:
        {
            switch (wParam)
            {
                case SIZE_MAXIMIZED:
                    this->Maximized(this, EventArgs::Empty);
                    break;

                case SIZE_MINIMIZED:
                    this->Minimized(this, EventArgs::Empty);
                    break;

                case SIZE_RESTORED:
                    this->Restored(this, EventArgs::Empty);
                    break;

                default:
                    this->Sized(this, gcnew SizedEventArgs(LOWORD(lParam), HIWORD(lParam)));
                    break;
            }

            return IntPtr::Zero;
        }
    }

    return IntPtr(::DefWindowProcW(hWnd, uMsg, wParam, lParam));
}


IntPtr Window::RaiseWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    IntPtr myHandle = IntPtr(hWnd);

    if (smWindows.ContainsKey(myHandle))
    {
        return smWindows[myHandle]->WindowProcedure(hWnd, uMsg, wParam, lParam);
    }

    return IntPtr(::DefWindowProcW(hWnd, uMsg, wParam, lParam));
}